"""
日志模块：PBPK建模系统日志管理

该模块实现了PBPK建模系统的全面日志管理，包括：
- 日志配置和初始化
- 不同级别的日志记录
- 文件和终端输出
- 性能监控
- 错误追踪
- 审计日志

作者：BlackCat@CPPO
版本：1.0.0
"""

import logging
import logging.config
import os
import sys
import json
from datetime import datetime
from pathlib import Path
import traceback
from typing import Dict, Any, Optional
import yaml

class PBPKLogger:
    """
    PBPK建模系统专用日志管理器
    
    提供全面的日志记录功能，支持：
    - 多级别日志记录（DEBUG, INFO, WARNING, ERROR, CRITICAL）
    - 文件和终端同步输出
    - 结构化日志格式
    - 性能监控
    - 错误追踪和调试
    """
    
    def __init__(self, config: Dict[str, Any] = None, name: str = "PBPK"):
        """
        初始化PBPK日志管理器
        
        参数:
            config (dict): 日志配置字典，包含：
                - level: 日志级别 (DEBUG/INFO/WARNING/ERROR/CRITICAL)
                - log_dir: 日志文件目录
                - max_file_size: 单个日志文件最大大小 (MB)
                - backup_count: 保留的备份文件数量
                - console_output: 是否输出到控制台
                - format: 日志格式
            name (str): 日志器名称
        """
        if config is None:
            config = {}
        elif isinstance(config, str):
            config = {'log_dir': config}
        
        self.config = {**self._get_default_config(), **config}
        self.name = name
        self.logger = None
        self.setup_logging()
        
    def _get_default_config(self) -> Dict[str, Any]:
        """获取默认日志配置"""
        return {
            'level': 'INFO',
            'log_dir': './logs',
            'max_file_size': 10,  # MB
            'backup_count': 5,
            'console_output': True,
            'format': '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
        }
    
    def setup_logging(self):
        """设置日志配置"""
        log_dir = Path(self.config['log_dir'])
        log_dir.mkdir(parents=True, exist_ok=True)
        
        # 创建日志配置
        logging_config = {
            'version': 1,
            'disable_existing_loggers': False,
            'formatters': {
                'standard': {
                    'format': self.config['format'],
                    'datefmt': '%Y-%m-%d %H:%M:%S'
                },
                'detailed': {
                    'format': '%(asctime)s - %(name)s - %(levelname)s - %(filename)s:%(lineno)d - %(funcName)s() - %(message)s',
                    'datefmt': '%Y-%m-%d %H:%M:%S'
                },
                'json': {
                    'format': '{"timestamp": "%(asctime)s", "level": "%(levelname)s", "module": "%(name)s", "message": "%(message)s", "function": "%(funcName)s", "line": %(lineno)d}',
                    'datefmt': '%Y-%m-%d %H:%M:%S'
                }
            },
            'handlers': {
                'console': {
                    'class': 'logging.StreamHandler',
                    'level': 'DEBUG' if self.config['console_output'] else 'CRITICAL',
                    'formatter': 'standard',
                    'stream': 'ext://sys.stdout'
                },
                'file': {
                    'class': 'logging.handlers.RotatingFileHandler',
                    'level': 'DEBUG',
                    'formatter': 'detailed',
                    'filename': str(log_dir / 'pbpk_modeling.log'),
                    'maxBytes': self.config['max_file_size'] * 1024 * 1024,
                    'backupCount': self.config['backup_count'],
                    'encoding': 'utf8'
                },
                'error_file': {
                    'class': 'logging.handlers.RotatingFileHandler',
                    'level': 'ERROR',
                    'formatter': 'detailed',
                    'filename': str(log_dir / 'pbpk_errors.log'),
                    'maxBytes': self.config['max_file_size'] * 1024 * 1024,
                    'backupCount': self.config['backup_count'],
                    'encoding': 'utf8'
                },
                'performance_file': {
                    'class': 'logging.handlers.RotatingFileHandler',
                    'level': 'INFO',
                    'formatter': 'json',
                    'filename': str(log_dir / 'pbpk_performance.log'),
                    'maxBytes': self.config['max_file_size'] * 1024 * 1024,
                    'backupCount': self.config['backup_count'],
                    'encoding': 'utf8'
                }
            },
            'loggers': {
                'PBPK': {
                    'level': self.config['level'],
                    'handlers': ['console', 'file', 'error_file'],
                    'propagate': False
                },
                'PBPK.Performance': {
                    'level': 'INFO',
                    'handlers': ['performance_file'],
                    'propagate': False
                }
            },
            'root': {
                'level': 'WARNING',
                'handlers': ['console', 'file']
            }
        }
        
        # 应用配置
        logging.config.dictConfig(logging_config)
        self.logger = logging.getLogger('PBPK')
        
        # 记录启动信息
        self.info("PBPK日志系统初始化完成")
        self.debug(f"日志目录: {log_dir}")
        self.debug(f"日志级别: {self.config['level']}")
    
    def log_model_start(self, model_name: str, parameters: Dict[str, Any]):
        """记录模型启动信息"""
        self.info(f"启动{model_name}模型")
        self.debug(f"模型参数: {json.dumps(parameters, indent=2, default=str)}")
    
    def log_model_completion(self, model_name: str, execution_time: float, results_summary: Dict[str, Any]):
        """记录模型完成信息"""
        self.info(f"{model_name}模型执行完成")
        self.performance_info({
            'model': model_name,
            'execution_time': execution_time,
            'results_summary': results_summary
        })
    
    def log_error(self, error_type: str, error_message: str, traceback_info: str = None):
        """记录错误信息"""
        error_details = {
            'error_type': error_type,
            'error_message': error_message,
            'traceback': traceback_info or traceback.format_exc()
        }
        self.error(f"错误发生: {error_type} - {error_message}")
        self.debug(f"错误详情: {json.dumps(error_details, indent=2)}")
    
    def log_warning(self, warning_type: str, warning_message: str, context: Dict[str, Any] = None):
        """记录警告信息"""
        warning_details = {
            'warning_type': warning_type,
            'warning_message': warning_message,
            'context': context or {}
        }
        self.warning(f"警告: {warning_type} - {warning_message}")
        self.debug(f"警告详情: {json.dumps(warning_details, indent=2)}")
    
    def log_parameter_validation(self, module_name: str, parameters: Dict[str, Any], validation_result: bool, errors: list = None):
        """记录参数验证信息"""
        validation_info = {
            'module': module_name,
            'validation_result': validation_result,
            'parameters_count': len(parameters),
            'errors': errors or []
        }
        if validation_result:
            self.info(f"{module_name}参数验证通过")
        else:
            self.error(f"{module_name}参数验证失败: {errors}")
        self.debug(f"验证详情: {json.dumps(validation_info, indent=2)}")
    
    def log_calculation_step(self, step_name: str, inputs: Dict[str, Any], outputs: Dict[str, Any], execution_time: float = None):
        """记录计算步骤信息"""
        step_info = {
            'step': step_name,
            'inputs': inputs,
            'outputs': outputs,
            'execution_time': execution_time
        }
        self.debug(f"计算步骤: {step_name}")
        self.performance_debug(step_info)
    
    def log_population_data(self, population_group: str, sample_size: int, demographics: Dict[str, Any]):
        """记录人口数据信息"""
        population_info = {
            'group': population_group,
            'sample_size': sample_size,
            'demographics': demographics
        }
        self.info(f"加载人口数据: {population_group}, 样本数: {sample_size}")
        self.debug(f"人口统计: {json.dumps(population_info, indent=2)}")
    
    def log_visualization_data(self, chart_type: str, data_points: int, chart_config: Dict[str, Any]):
        """记录可视化数据信息"""
        viz_info = {
            'chart_type': chart_type,
            'data_points': data_points,
            'config': chart_config
        }
        self.info(f"生成图表: {chart_type}, 数据点: {data_points}")
        self.debug(f"图表配置: {json.dumps(viz_info, indent=2)}")
    
    def log_file_operation(self, operation: str, file_path: str, success: bool, error: str = None):
        """记录文件操作信息"""
        file_info = {
            'operation': operation,
            'file_path': file_path,
            'success': success,
            'error': error
        }
        if success:
            self.info(f"文件操作成功: {operation} - {file_path}")
        else:
            self.error(f"文件操作失败: {operation} - {file_path} - {error}")
        self.debug(f"文件操作详情: {json.dumps(file_info, indent=2)}")
    
    def log_performance_metrics(self, operation: str, start_time: float, end_time: float, memory_usage: float = None):
        """记录性能指标"""
        duration = end_time - start_time
        perf_info = {
            'operation': operation,
            'duration': duration,
            'memory_usage': memory_usage,
            'timestamp': datetime.now().isoformat()
        }
        self.performance_info(perf_info)
    
    def debug(self, message: str):
        """记录调试信息"""
        if self.logger:
            self.logger.debug(message)
    
    def info(self, message: str):
        """记录信息"""
        if self.logger:
            self.logger.info(message)
    
    def warning(self, message: str):
        """记录警告"""
        if self.logger:
            self.logger.warning(message)
    
    def error(self, message: str):
        """记录错误"""
        if self.logger:
            self.logger.error(message)
    
    def critical(self, message: str):
        """记录严重错误"""
        if self.logger:
            self.logger.critical(message)
    
    def performance_info(self, data: Dict[str, Any]):
        """记录性能信息到专用日志"""
        perf_logger = logging.getLogger('PBPK.Performance')
        perf_logger.info(json.dumps(data, default=str))
    
    def performance_debug(self, data: Dict[str, Any]):
        """记录性能调试信息"""
        perf_logger = logging.getLogger('PBPK.Performance')
        perf_logger.debug(json.dumps(data, default=str))
    
    def get_log_summary(self, log_level: str = "INFO") -> Dict[str, int]:
        """获取日志统计摘要"""
        log_file = Path(self.config['log_dir']) / 'pbpk_modeling.log'
        if not log_file.exists():
            return {}
        
        level_counts = {}
        with open(log_file, 'r', encoding='utf-8') as f:
            for line in f:
                if log_level in line:
                    level_counts[log_level] = level_counts.get(log_level, 0) + 1
        
        return level_counts
    
    def rotate_logs(self):
        """手动轮转日志文件"""
        for handler in logging.getLogger('PBPK').handlers:
            if hasattr(handler, 'doRollover'):
                handler.doRollover()
        self.info("日志文件已轮转")

class ModelExecutionLogger:
    """
    模型执行上下文管理器
    
    用于自动记录模型执行的开始和结束
    """
    
    def __init__(self, logger: PBPKLogger, model_name: str, parameters: Dict[str, Any]):
        self.logger = logger
        self.model_name = model_name
        self.parameters = parameters
        self.start_time = None
    
    def __enter__(self):
        import time
        self.start_time = time.time()
        self.logger.log_model_start(self.model_name, self.parameters)
        return self
    
    def __exit__(self, exc_type, exc_val, exc_tb):
        import time
        end_time = time.time()
        execution_time = end_time - self.start_time
        
        if exc_type is None:
            self.logger.log_model_completion(self.model_name, execution_time, {})
        else:
            self.logger.log_error(
                exc_type.__name__, 
                str(exc_val), 
                traceback.format_exception(exc_type, exc_val, exc_tb)
            )

# 全局日志实例
_global_logger = None

def get_logger(config: Dict[str, Any] = None) -> PBPKLogger:
    """
    获取全局日志实例
    
    参数:
        config (dict): 日志配置
        
    返回:
        PBPKLogger: 日志实例
    """
    global _global_logger
    if _global_logger is None:
        _global_logger = PBPKLogger(config)
    return _global_logger

def setup_logging_from_file(config_file: str) -> PBPKLogger:
    """
    从配置文件设置日志
    
    参数:
        config_file (str): 配置文件路径
        
    返回:
        PBPKLogger: 日志实例
    """
    try:
        with open(config_file, 'r', encoding='utf-8') as f:
            if config_file.endswith('.yaml') or config_file.endswith('.yml'):
                config = yaml.safe_load(f)
            else:
                config = json.load(f)
        return get_logger(config.get('logging', {}))
    except Exception as e:
        print(f"加载日志配置文件失败: {e}")
        return get_logger()

# 快捷函数
def debug(message: str):
    """记录调试信息"""
    get_logger().debug(message)

def info(message: str):
    """记录信息"""
    get_logger().info(message)

def warning(message: str):
    """记录警告"""
    get_logger().warning(message)

def error(message: str):
    """记录错误"""
    get_logger().error(message)

def critical(message: str):
    """记录严重错误"""
    get_logger().critical(message)

if __name__ == "__main__":
    # 测试日志功能
    logger = get_logger()
    
    logger.info("测试日志系统")
    logger.debug("调试信息")
    logger.warning("警告信息")
    
    # 测试模型执行日志
    with ModelExecutionLogger(logger, "测试模型", {"test": "value"}):
        logger.info("执行模型计算...")
        time.sleep(0.1)
    
    print("日志测试完成")